package http_client

import (
	"context"
	"errors"
	"git.mycaigou.com/gfyx/common/cache"
	"git.myscrm.cn/golang/stark/v4"
	"github.com/parnurzeal/gorequest"
	"strconv"
	"strings"
)

const (
	BIGDATA_API_HOST      = "bigdata_api_host"
	BIGDATA_API_APPID     = "bigdata_api_appid"
	BIGDATA_API_KEY       = "bigdata_api_key"
	BIGDATA_API_SECRET    = "bigdata_api_secret"
	BIGDATA_API_NAMESPACE = "bigdata_api_namespace"
	// 目前多个端共用一个redis 缓存key
	BIGDATA_API_CACHE_KEY = "gfyx:bigdata:token"
)

type BigdataApiHttpClient struct {
	client *HttpClientTool
}

// NewBigdataApiHttpClient
// 采购平台供应商开放服务http client工具类
func NewBigdataApiHttpClient() *BigdataApiHttpClient {
	return &BigdataApiHttpClient{
		client: New(),
	}
}

// Get
// 数芯数据查询服务Get请求
func (c *BigdataApiHttpClient) Get(ctx context.Context, path string, params map[string]interface{}) *BigdataApiHttpClient {
	url := stark.Configer.(stark.ApolloConfigerIface).GetStringValue(BIGDATA_API_HOST, "")
	nameSpace := stark.Configer.(stark.ApolloConfigerIface).GetStringValue(BIGDATA_API_NAMESPACE, "")
	url += "/openapi/" + nameSpace + path
	token, err := c.GetToken(ctx)
	if err != nil {
		stark.Logger.Errorf(ctx, "bigdata获取token失败，err:%+v", err)
	}
	params["jwt"] = token
	url = JoinQueryStr(url, params)

	c.client = c.client.Get(ctx, url, nil)
	return c
}

func (c *BigdataApiHttpClient) Post(ctx context.Context, path string, params map[string]interface{}, header map[string]string) *BigdataApiHttpClient {
	url := stark.Configer.(stark.ApolloConfigerIface).GetStringValue(BIGDATA_API_HOST, "")
	nameSpace := stark.Configer.(stark.ApolloConfigerIface).GetStringValue(BIGDATA_API_NAMESPACE, "")
	url += "/openapi/" + nameSpace + path
	token, err := c.GetToken(ctx)

	if err != nil {
		stark.Logger.Errorf(ctx, "bigdata获取token失败，err:%+v", err)
	}
	requestParams := map[string]interface{}{
		"jwt": token,
	}
	urlWithJwt := JoinQueryStr(url, requestParams)

	c.client.Post(ctx, urlWithJwt, params, header)
	c.tokenFailPostRetry(ctx, header, url, params)

	return c
}

// token失效时接口重试
func (c *BigdataApiHttpClient) tokenFailPostRetry(ctx context.Context, header map[string]string, url string, params map[string]interface{}) {
	invalidSignatureResponse := &InvalidSignatureResponse{}
	_, _, _ = c.client.EndStruct(invalidSignatureResponse)
	// 鉴权失败 {"message":"Invalid signature"}
	if strings.Index(invalidSignatureResponse.Message, "Invalid signature") != -1 {
		// 删除缓存重试
		c.clearCacheToken(ctx)
		token, err := c.GetToken(ctx)

		if err != nil {
			stark.Logger.Errorf(ctx, "bigdata获取token失败，err:%+v", err)
		}
		requestParams := map[string]interface{}{
			"jwt": token,
		}
		urlWithJwt := JoinQueryStr(url, requestParams)

		c.client.Post(ctx, urlWithJwt, params, header)
	}
}

// 清除缓存的token
func (c *BigdataApiHttpClient) clearCacheToken(ctx context.Context) {
	cacheKey := BIGDATA_API_CACHE_KEY
	redisClient, _ := cache.Redis()
	//删缓存
	redisClient.Del(ctx, cacheKey)
}

type InvalidSignatureResponse struct {
	Message string `json:"message"`
}

type TokenResult struct {
	RequestId string `json:"requestId"`
	ErrCode   int32  `json:"errCode"`
	ErrMsg    string `json:"errMsg"`
	Data      struct {
		Token     string `json:"token"`
		ExpiresIn string `json:"expires_in"`
	} `json:"data"`
}

func (c *BigdataApiHttpClient) GetToken(ctx context.Context) (string, error) {
	cacheKey := BIGDATA_API_CACHE_KEY
	cRedis, _ := cache.Redis()
	token, err := cRedis.Get(ctx, cacheKey)
	if err != nil {
		stark.Logger.Errorf(ctx, "bigdata 读缓存失败，err: %+v", err)
	} else if token != "" {
		return token, nil
	}

	appid := stark.Configer.(stark.ApolloConfigerIface).GetStringValue(BIGDATA_API_APPID, "")
	app_key := stark.Configer.(stark.ApolloConfigerIface).GetStringValue(BIGDATA_API_KEY, "")
	app_secret := stark.Configer.(stark.ApolloConfigerIface).GetStringValue(BIGDATA_API_SECRET, "")
	params := map[string]interface{}{}
	params["app_id"] = appid
	params["app_key"] = app_key
	params["app_secret"] = app_secret

	url := stark.Configer.(stark.ApolloConfigerIface).GetStringValue(BIGDATA_API_HOST, "")
	url = url + "/open-api/tag/archive-data/token_api/get"
	url = JoinQueryStr(url, params)
	client := c.client.Get(ctx, url, nil)

	var tokenResult TokenResult
	resp, _, errs := client.EndStruct(&tokenResult)
	if len(errs) > 0 {
		stark.Logger.Errorf(ctx, "bigdata get token 失败，err:%+v", errs)
		return "", errs[0]
	}
	stark.Logger.Infof(ctx, "bigdata  get token, response: %+v, data:%+v", resp, tokenResult)

	if tokenResult.ErrCode != 1 {
		stark.Logger.Errorf(ctx, "bigdata get token 失败，error_code:%s, err_msg:%s", tokenResult.ErrCode, tokenResult.ErrMsg)
		return "", errors.New("获取bigdata token失败")
	}
	if tokenResult.Data.Token == "" {
		stark.Logger.Errorf(ctx, "bigdata get token 失败，token为空")
		return "", errors.New("获取bigdata token失败")
	}

	expireTime, _ := strconv.Atoi(tokenResult.Data.ExpiresIn)
	if expireTime < 60 {
		expireTime = 7200 - 60
	} else {
		expireTime = expireTime - 60
	}
	_, err = cRedis.Set(ctx, cacheKey, tokenResult.Data.Token, true, expireTime)
	if err != nil {
		stark.Logger.Errorf(ctx, "bigdata set token 缓存失败，err:%+v", err)
	}

	return tokenResult.Data.Token, nil
}

// EndStruct
// convert interface(v) to struct
func (c *BigdataApiHttpClient) EndStruct(v interface{}) (gorequest.Response, []byte, []error) {
	return c.client.EndStruct(v)
}

// BigDataResponse 分页数据
type BigDataResponse struct {
	RequestId string `json:"requestId"`
	ErrCode   int    `json:"errCode"`
	ErrMsg    string `json:"errMsg"`
	Data      struct {
		TotalNum int                      `json:"totalNum"`
		PageSize int                      `json:"pageSize"`
		PageNum  int                      `json:"pageNum"`
		Rows     []map[string]interface{} `json:"rows"`
	} `json:"data"`
	Sql interface{} `json:"sql"`
}

// BigDataResponseList 不分页数据
type BigDataResponseList struct {
	RequestId string                   `json:"requestId"`
	ErrCode   int                      `json:"errCode"`
	ErrMsg    string                   `json:"errMsg"`
	Data      []map[string]interface{} `json:"data"`
	Sql       interface{}              `json:"sql"`
}

// EndStruct
// convert interface(v) to struct
func (c *BigdataApiHttpClient) ResStruct(result interface{}) *BigDataResponse {
	bidDataResponse := &BigDataResponse{}
	_, _, err := c.client.EndStruct(bidDataResponse)
	if err != nil {
		stark.Logger.Errorf(nil, "json转结构体失败，err:%+v", err)
	}
	return bidDataResponse
}
